﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Windows.Foundation;
using Windows.System;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using XamlInspector.Foundation;
using XamlInspector.Model;
using XamlInspector.Presentation.Controls;

namespace XamlInspector.Presentation
{
    public sealed partial class VisualTreeControl
    {
        private readonly Popup _highlightPopup = new Popup();
        private FrameworkElement _highlightedElement;
        private readonly Rectangle _highlightRectangle;
        private readonly Dictionary<XamlElement, VisualTreeItem> _allVisualTreeItems = new Dictionary<XamlElement, VisualTreeItem>();

        public VisualTreeControl()
        {
            InitializeComponent();
            _highlightPopup.IsOpen = true;
            VisualTreeItems = new MergeableObservableCollection<VisualTreeItem>();
            var coreWindow = CoreWindow.GetForCurrentThread();
            if (coreWindow != null)
            {
                coreWindow.PointerMoved += CoreWindowOnPointerMoved;
            }

            _highlightRectangle = new Rectangle
            {
                StrokeThickness = 2,
                Stroke = new SolidColorBrush(Colors.Red),
            };
            _highlightPopup.Child = _highlightRectangle;

            var timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(50);
            timer.Tick += (s, e) => UpdateHighlightPosition();
            timer.Start();
        }

        private void CoreWindowOnPointerMoved(CoreWindow sender, PointerEventArgs e)
        {
            if (e.KeyModifiers == VirtualKeyModifiers.Control)
            {
                TreeModel.AllElements.ForEach(x => x.HitTestOverride = true);
                var hitTest = VisualTreeHelper.FindElementsInHostCoordinates(e.CurrentPoint.Position, TreeModel.Root.Element);
                var eElement = hitTest.FirstOrDefault() as FrameworkElement;
                if (eElement != null)
                {
                    var xamlElement = TreeModel.XamlElementFromUIElement(eElement);
                    SelectedElement = xamlElement;
                }
                TreeModel.AllElements.ForEach(x => x.HitTestOverride = false);
            }
        }

        public XamlTreeModel TreeModel
        {
            get { return (XamlTreeModel)GetValue(TreeModelProperty); }
            set { SetValue(TreeModelProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TreeModel.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TreeModelProperty =
            DependencyProperty.Register("TreeModel", typeof(XamlTreeModel), typeof(VisualTreeControl), new PropertyMetadata(null, OnTreeModelChanged));

        public XamlElement SelectedElement
        {
            get { return (XamlElement)GetValue(SelectedElementProperty); }
            set { SetValue(SelectedElementProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedElement.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedElementProperty =
            DependencyProperty.Register("SelectedElement", typeof(XamlElement), typeof(VisualTreeControl), new PropertyMetadata(null, OnSelectedElementChanged));

        private static void OnSelectedElementChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = (VisualTreeControl)sender;
            var xamlElement = e.NewValue as XamlElement;

            if (xamlElement != null)
            {
                control.HighlightElement(xamlElement.Element);

                var visualTreeItem = control._allVisualTreeItems[xamlElement];
                visualTreeItem.ExpandTree();

                control.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => control.listBox.ScrollIntoView(visualTreeItem));
            }
        }

        private static void OnTreeModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (VisualTreeControl)d;
            var treeModel = e.NewValue as XamlTreeModel;

            if (treeModel != null)
            {
                treeModel.AllElements.CollectionChanged += (s, a) => control.UpdateTree();
                control.UpdateTree();
            }
        }

        public MergeableObservableCollection<VisualTreeItem> VisualTreeItems
        {
            get { return (MergeableObservableCollection<VisualTreeItem>)GetValue(VisualTreeItemsProperty); }
            set { SetValue(VisualTreeItemsProperty, value); }
        }

        public static readonly DependencyProperty VisualTreeItemsProperty =
            DependencyProperty.Register("VisualTreeItems", typeof(MergeableObservableCollection<VisualTreeItem>), typeof(VisualTreeControl), new PropertyMetadata(null));

        private void UpdateTree()
        {
            if (TreeModel == null)
            {
                _allVisualTreeItems.Clear();
                return;
            }

            var allElements = TreeModel.AllElements.ToList();

            var elementsToRemove = _allVisualTreeItems.Keys.Except(allElements).ToList();
            elementsToRemove.ForEach(e => VisualTreeItems.Remove(GetOrCreateTreeItem(e)));
            elementsToRemove.ForEach(e => _allVisualTreeItems.Remove(e));

            var elementsToAdd = allElements.Except(_allVisualTreeItems.Keys).ToList();
            elementsToAdd.ForEach(e => GetOrCreateTreeItem(e));

            var rootItem = GetOrCreateTreeItem(TreeModel.Root);
            if (VisualTreeItems.Contains(rootItem) == false)
            {
                VisualTreeItems.Add(rootItem);
            }

            SelectedElement = TreeModel.Root;
        }

        private VisualTreeItem GetOrCreateTreeItem(XamlElement element)
        {
            if (_allVisualTreeItems.ContainsKey(element))
            {
                return _allVisualTreeItems[element];
            }

            var treeItem = new VisualTreeItem(VisualTreeItems, GetOrCreateTreeItem, element);
            _allVisualTreeItems.Add(element, treeItem);
            return treeItem;
        }

        private void HighlightElement(FrameworkElement element)
        {
            if (element == null)
            {
                _highlightPopup.IsOpen = false;
                return;
            }

            _highlightedElement = element;
            _highlightPopup.IsOpen = true;

            UpdateHighlightPosition();
        }

        private void UpdateHighlightPosition()
        {
            if (_highlightPopup.IsOpen && _highlightedElement != null)
            {
                var gt = _highlightedElement.TransformToVisual(_highlightPopup);
                Point topLeftOfElement = gt.TransformPoint(new Point(0, 0));

                _highlightRectangle.Width = _highlightedElement.ActualWidth;
                _highlightRectangle.Height = _highlightedElement.ActualHeight;

                _highlightPopup.Width = _highlightedElement.ActualWidth;
                _highlightPopup.Height = _highlightedElement.ActualHeight;
                _highlightPopup.VerticalOffset = topLeftOfElement.Y;
                _highlightPopup.HorizontalOffset = topLeftOfElement.X;
            }
        }
    }
}
